package com.own.component.common.util.list;

import com.google.common.collect.Lists;
import com.google.common.math.IntMath;
import com.own.component.common.util.function.LambdaFunction;
import com.own.constant.ConstantConvert;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.google.common.base.Preconditions.checkElementIndex;

/**
 * ListUtil
 *
 * @author chenxueli
 * @date 2022-06-16 15:03:00
 */
@Slf4j
public final class ListUtil {

    /**
     * 数据去重
     *
     * @param list 原列表
     * @param <T>  列表的类型
     * @return 新列表
     */
    public static <T> List<T> distinct(List<T> list) {
        var resultList = new ArrayList<T>();
        list.forEach(item -> {
            if (!resultList.contains(item)) {
                resultList.add(item);
            }
        });
        return resultList;
    }

    /**
     * 数据去重
     *
     * @param list         原列表
     * @param keyExtractor 去重关系
     * @param <T>          列表的类型
     * @return 新列表
     */
    public static <T> List<T> distinct(List<T> list, Function<? super T, Object> keyExtractor) {
        return list.stream().filter(LambdaFunction.distinctByKey(keyExtractor)).collect(Collectors.toList());
    }

    /**
     * 获取公共的数据
     *
     * @param list 列表
     * @return 公共的数据
     */
    public static <T> Optional<List<T>> retain(List<List<T>> list) {
        if (list.isEmpty()) {
            return Optional.empty();
        }
        if (list.size() == 1) {
            return Optional.of(list.getFirst());
        }
        var map = new HashMap<T, Integer>();
        list.forEach(values -> values.forEach(item -> {
            map.put(item, map.getOrDefault(item, 0) + 1);
        }));
        // 去除次数等于当前列表的数量
        return Optional.of(map.entrySet().stream().filter(entry -> entry.getValue() == list.size()).map(Map.Entry::getKey).toList());
    }

    /**
     * 获取非空的列表索引
     *
     * @param list 列表
     * @param fun  判断方法
     * @param <T>  列表的类型
     * @return 索引
     */
    public static <T> List<Integer> listNonValueIndex(List<T> list, Function<T, String> fun) {
        var array = new ArrayList<Integer>();
        for (int i = 0; i < list.size(); i++) {
            var value = fun.apply(list.get(i));
            if (StringUtils.isNotBlank(value)) {
                array.add(i);
            }
        }
        return array;
    }

    /**
     * 拆分数组
     *
     * @param list   原数组
     * @param length 每项长度
     * @param <T>    类型
     * @return 返回列表
     */
    public static <T> List<List<T>> groupList(List<T> list, int length) {
        return Lists.partition(list, length);
    }

    /**
     * 拆分数组
     *
     * @param list   原数组
     * @param length 每项长度
     * @param fun    特定事件
     * @param <T>    类型
     * @return 返回列表
     */
    public static <T, V> List<V> groupList(List<T> list, int length, Function<List<T>, V> fun) {
        return new CustomPartition<>(list, length, fun);
    }

    /**
     * 排序
     */
    public static final class Sort {

        /**
         * 按照指定的顺序排序列表
         *
         * @param list      原列表（修改后的列表）
         * @param keyList   排序的顺序键
         * @param extractor 匹配的规则键
         * @param <T>       列表的类型
         * @param <R>       顺序键的类型
         */
        public static <T, R> void sort(List<T> list, List<R> keyList, Function<T, R> extractor) {
            var map = list.stream().collect(Collectors.groupingBy(extractor));
            var resultList = new ArrayList<T>();
            distinct(keyList).forEach(key -> {
                var itemList = map.get(key);
                if (itemList != null) {
                    itemList.forEach(item -> {
                        resultList.add(item);
                        list.remove(item);
                    });
                }
            });
            list.addAll(0, resultList);
        }

        /**
         * 列表设置排序序号
         *
         * @param list 原列表信息
         * @param set  设置方法
         * @param <T>  类型
         */
        public static <T> void setSortOrder(List<T> list, BiConsumer<T, Integer> set) {
            setSortOrder(list, set, 0);
        }

        /**
         * 列表设置排序序号
         *
         * @param list  原列表信息
         * @param set   设置方法
         * @param start 起始值
         * @param <T>   类型
         */
        public static <T> void setSortOrder(List<T> list, BiConsumer<T, Integer> set, int start) {
            for (var i = 0; i < list.size(); i++) {
                set.accept(list.get(i), i + start);
            }
        }

        /**
         * 设置排序
         *
         * @param list       原列表数据
         * @param comparator 排序方法
         * @param set        设置序号的方法
         * @param <T>        类型
         */
        public static <T> void collate(List<T> list, Comparator<T> comparator, BiConsumer<T, Integer> set) {
            collate(list, comparator, set, 0);
        }

        /**
         * 设置排序
         *
         * @param list       原列表数据
         * @param comparator 排序方法
         * @param set        设置序号的方法
         * @param start      开始位置
         * @param <T>        类型
         */
        public static <T> void collate(List<T> list, Comparator<T> comparator, BiConsumer<T, Integer> set, int start) {
            // 排序
            var resultList = list.stream().sorted(comparator).toList();
            setSortOrder(resultList, set, start);
        }

        /**
         * 移除重复的排名信息
         *
         * @param list     列表信息
         * @param get      获取排序数据
         * @param getOrder 获取排名数据
         * @param set      设置排名数据
         * @param <T>      类型
         */
        public static <T> void removeRepeatCollate(List<T> list, Function<T, Number> get, Function<T, Integer> getOrder, BiConsumer<T, Integer> set) {
            var resultList = list.stream().sorted(Comparator.comparing(getOrder)).toList();
            Number n = null;
            Integer order = null;
            for (var item : resultList) {
                var n2 = get.apply(item);
                if (n != null && n.equals(n2)) {
                    set.accept(item, order);
                    continue;
                }
                n = n2;
                order = getOrder.apply(item);
            }
        }

    }

    /**
     * 字符串转列表
     */
    public static final class Spilt {

        /**
         * 默认的分隔符
         */
        private static final String DEFAULT_SEPARATE = ",";

        /**
         * 数据按按照长度分组
         *
         * @param str      元字符串
         * @param splitLen 每个单元的长度
         * @return 分组完成的列表
         */
        public static List<String> str(String str, int splitLen) {
            var count = str.length() / splitLen + (str.length() % splitLen == 0 ? 0 : 1);
            var resultList = new ArrayList<String>();
            for (int i = 0; i < count; i++) {
                if (str.length() <= splitLen) {
                    resultList.add(str);
                } else {
                    resultList.add(str.substring(0, splitLen));
                    str = str.substring(splitLen);
                }
            }
            return resultList;
        }

        /**
         * 字符串转列表
         *
         * @param content 字符串
         * @return 列表
         */
        public static List<String> to(String content) {
            return to(content, DEFAULT_SEPARATE, ConstantConvert.STRING_TO_STRING);
        }

        /**
         * 字符串转列表
         *
         * @param content  字符串
         * @param separate 分隔符
         * @return 列表
         */
        public static List<String> to(String content, String separate) {
            return to(content, separate, ConstantConvert.STRING_TO_STRING);
        }

        /**
         * 字符串转列表
         *
         * @param content  字符串
         * @param function 转换函数
         * @param <T>      列表元素类型
         * @return 列表
         */
        public static <T> List<T> to(String content, Function<String, T> function) {
            return to(content, DEFAULT_SEPARATE, function);
        }

        /**
         * 字符串转列表
         *
         * @param content  字符串
         * @param separate 分隔符
         * @param function 转换函数
         * @param <T>      列表元素类型
         * @return 列表
         */
        public static <T> List<T> to(String content, String separate, Function<String, T> function) {
            var list = new ArrayList<T>();
            if (StringUtils.isBlank(content)) {
                return list;
            }
            return Arrays.stream(content.split(separate)).filter(StringUtils::isNotBlank).map(function).toList();
        }

        /**
         * 字符串转整型列表
         *
         * @param content 字符串
         * @return 列表
         */
        public static List<Integer> toInteger(String content) {
            return to(content, DEFAULT_SEPARATE, ConstantConvert.STRING_TO_INTEGER);
        }

        /**
         * 字符串转整型列表
         *
         * @param content  字符串
         * @param separate 分隔符
         * @return 列表
         */
        public static List<Integer> toInteger(String content, String separate) {
            return to(content, separate, ConstantConvert.STRING_TO_INTEGER);
        }

        /**
         * 字符串转长整型列表
         *
         * @param content 字符串
         * @return 列表
         */
        public static List<Long> toLong(String content) {
            return to(content, DEFAULT_SEPARATE, ConstantConvert.STRING_TO_LONG);
        }

        /**
         * 字符串转长整型列表
         *
         * @param content  字符串
         * @param separate 分隔符
         * @return 列表
         */
        public static List<Long> toLong(String content, String separate) {
            return to(content, separate, ConstantConvert.STRING_TO_LONG);
        }

    }

    /**
     * 打乱
     */
    public static final class Random {

        /**
         * 长度比例1
         */
        private static final BigDecimal LENGTH_1 = new BigDecimal("0.6");
        /**
         * 长度比例2
         */
        private static final BigDecimal LENGTH_2 = new BigDecimal("0.4");

        /**
         * 数组中随机取值
         *
         * @param list   原列表
         * @param number 需要获取的场地
         * @param <T>    类型
         * @return 新列表
         */
        public static <T> List<T> random(List<T> list, int number) {
            if (list == null || number <= 0 || number >= list.size()) {
                return list;
            }
            var tempList = new ArrayList<>(list);
            Collections.shuffle(tempList);
            var resultList = tempList.stream().limit(number).collect(Collectors.toList());
            Collections.shuffle(resultList);
            return resultList;
        }

        /**
         * 数组中随机取值
         *
         * @param list      原列表
         * @param extractor 排序字段
         * @param number    需要获取的场地
         * @param <T>       类型
         * @param <R>       排序类型
         * @return 新列表
         */
        public static <T, R extends Comparable<? super R>> List<T> random(List<T> list, Function<T, R> extractor, int number) {
            return random(list, extractor, number, true);
        }

        /**
         * 数组中随机取值
         *
         * @param list      原列表
         * @param extractor 排序字段
         * @param number    需要获取的场地
         * @param desc      是否需要降序
         * @param <T>       类型
         * @param <R>       排序类型
         * @return 新列表
         */
        public static <T, R extends Comparable<? super R>> List<T> random(List<T> list, Function<T, R> extractor, int number, boolean desc) {
            if (list == null || extractor == null || number <= 0 || number >= list.size()) {
                return list;
            }
            var length = getLength(number);
            var temp = desc
                    ? list.stream().sorted(Comparator.comparing(extractor).reversed()).toList()
                    : list.stream().sorted(Comparator.comparing(extractor)).toList();
            // 取出满足条件的一级内容
            var resultList1 = temp.stream().limit(length[0]).collect(Collectors.toSet());
            var tempList = new ArrayList<>(temp).subList(length[0], list.size());
            Collections.shuffle(tempList);
            var resultList2 = tempList.stream().limit(length[1]).collect(Collectors.toSet());
            var resultList = Stream.concat(resultList1.stream(), resultList2.stream()).collect(Collectors.toList());
            Collections.shuffle(resultList);
            return resultList;
        }

        /**
         * 获取长度
         *
         * @param length 总长度
         * @return 新的长度数组
         */
        private static int[] getLength(int length) {
            var temp = new BigDecimal(length);
            return new int[]{
                    temp.multiply(LENGTH_1).setScale(0, RoundingMode.HALF_UP).intValue(),
                    temp.multiply(LENGTH_2).setScale(0, RoundingMode.HALF_UP).intValue()
            };
        }

    }

    public static final class Tree {

        /**
         * 字符串链拼接的间隔符
         */
        private static final String CHAIN_SEPARATOR = "cs" + UUID.randomUUID().toString().replace("-", "") + "cs";

        /**
         * 获取树形列表数据
         *
         * @param list       原列表
         * @param idKey      标识上下级的key
         * @param fkIdKey    标识引用的键的key
         * @param childKey   标识子级列表的key
         * @param <TREE_VO>  对象类型
         * @param <KEY_TYPE> 键的类型
         * @return 新列表信息
         */
        public static <TREE_VO, KEY_TYPE> List<TREE_VO> build(
                List<TREE_VO> list,
                Function<TREE_VO, KEY_TYPE> idKey,
                Function<TREE_VO, KEY_TYPE> fkIdKey,
                Function<TREE_VO, List<TREE_VO>> childKey
        ) {
            if (list.isEmpty()) {
                return new ArrayList<>();
            }
            // 根据相关信息转成对应的map
            var parentMap = list.stream().collect(Collectors.toMap(idKey, Function.identity(), (v1, v2) -> v1));
            // 获取所有的没有父级的数据
            var rootKeyList = list.stream().map(fkIdKey).filter(key -> !parentMap.containsKey(key)).collect(Collectors.toSet());
            // 遍历所有的节点
            list.stream()
                    // 过滤不包含在内的数据
                    .filter(item -> parentMap.containsKey(fkIdKey.apply(item)))
                    // 将所有的值赋值到上一级菜单中
                    .forEach(item -> childKey.apply(parentMap.get(fkIdKey.apply(item))).add(item));
            // 遍历筛选出父节点为根节点的内容
            return list.stream().filter(item -> rootKeyList.contains(idKey.apply(item))).collect(Collectors.toList());
        }

        /**
         * 获取树形列表数据
         *
         * @param list       原列表
         * @param idKey      标识上下级的key
         * @param fkIdKey    标识引用的键的key
         * @param childKey   标识子级列表的key
         * @param <TREE_VO>  对象类型
         * @param <KEY_TYPE> 键的类型
         * @return 新列表信息
         */
        public static <TREE_VO, KEY_TYPE> List<TREE_VO> build(
                List<TREE_VO> list,
                Function<TREE_VO, KEY_TYPE> idKey,
                Function<TREE_VO, KEY_TYPE> fkIdKey,
                Function<TREE_VO, List<TREE_VO>> childKey,
                KEY_TYPE rootKey
        ) {
            if (list.isEmpty()) {
                return new ArrayList<>();
            }
            // 根据相关信息转成对应的map
            var parentMap = list.stream().collect(Collectors.toMap(idKey, Function.identity(), (v1, v2) -> v1));
            // 遍历所有的节点
            list.stream()
                    // 过滤不包含在内的数据
                    .filter(item -> parentMap.containsKey(fkIdKey.apply(item)))
                    // 将所有的值赋值到上一级菜单中
                    .forEach(item -> childKey.apply(parentMap.get(fkIdKey.apply(item))).add(item));
            // 遍历筛选出父节点为根节点的内容
            return list.stream().filter(item -> idKey.apply(item).equals(rootKey)).toList();
        }

        /**
         * 获取树形列表数据
         *
         * @param list       原列表
         * @param idKey      标识上下级的key
         * @param fkIdKey    标识引用的键的key
         * @param childKey   标识子级列表的key
         * @param levelKey   标识级别的key
         * @param rootLevel  层级
         * @param <TREE_VO>  对象类型
         * @param <KEY_TYPE> 键的类型
         * @return 新列表信息
         */
        public static <TREE_VO, KEY_TYPE> List<TREE_VO> build(
                List<TREE_VO> list,
                Function<TREE_VO, KEY_TYPE> idKey,
                Function<TREE_VO, KEY_TYPE> fkIdKey,
                Function<TREE_VO, List<TREE_VO>> childKey,
                Function<TREE_VO, Integer> levelKey,
                Integer rootLevel
        ) {
            if (list.isEmpty()) {
                return new ArrayList<>();
            }
            // 根据相关信息转成对应的map
            var parentMap = list.stream().collect(Collectors.toMap(idKey, Function.identity(), (v1, v2) -> v1));
            return buildTree(list, parentMap, fkIdKey, childKey, levelKey, rootLevel);
        }

        /**
         * 获取树形列表数据（选择最小的level）
         *
         * @param list       原列表
         * @param idKey      标识上下级的key
         * @param fkIdKey    标识引用的键的key
         * @param childKey   标识子级列表的key
         * @param levelKey   标识级别的key
         * @param <TREE_VO>  对象类型
         * @param <KEY_TYPE> 键的类型
         * @return 新列表信息
         */
        public static <TREE_VO, KEY_TYPE> List<TREE_VO> build(
                List<TREE_VO> list,
                Function<TREE_VO, KEY_TYPE> idKey,
                Function<TREE_VO, KEY_TYPE> fkIdKey,
                Function<TREE_VO, List<TREE_VO>> childKey,
                Function<TREE_VO, Integer> levelKey
        ) {
            if (list.isEmpty()) {
                return new ArrayList<>();
            }
            // 根据相关信息转成对应的map
            var parentMap = list.stream().collect(Collectors.toMap(idKey, Function.identity(), (v1, v2) -> v1));
            // 获取节点最小的值
            var minLevel = list.stream().map(levelKey).min(Integer::compareTo).orElse(null);
            if (minLevel == null) {
                return new ArrayList<>();
            }
            return buildTree(list, parentMap, fkIdKey, childKey, levelKey, minLevel);
        }

        /**
         * 构建树形结构
         *
         * @param list       原列表信息
         * @param parentMap  键对象信息
         * @param fkIdKey    标识引用的键的key
         * @param childKey   标识子级列表的key
         * @param levelKey   标识级别的key
         * @param rootLevel  层级
         * @param <TREE_VO>  树的类型
         * @param <KEY_TYPE> 键的类型
         * @return 树形列表数据
         */
        private static <TREE_VO, KEY_TYPE> List<TREE_VO> buildTree(
                List<TREE_VO> list,
                Map<KEY_TYPE, TREE_VO> parentMap,
                Function<TREE_VO, KEY_TYPE> fkIdKey,
                Function<TREE_VO, List<TREE_VO>> childKey,
                Function<TREE_VO, Integer> levelKey,
                Integer rootLevel
        ) {
            // 遍历所有的节点
            list.stream()
                    // 过滤是根节点的数据
                    .filter(item -> !rootLevel.equals(levelKey.apply(item)))
                    // 过滤不包含在内的数据
                    .filter(item -> parentMap.containsKey(fkIdKey.apply(item)))
                    // 将所有的值赋值到上一级菜单中
                    .forEach(item -> childKey.apply(parentMap.get(fkIdKey.apply(item))).add(item));
            // 遍历筛选出父节点为根节点的内容
            return list.stream().filter(item -> rootLevel.equals(levelKey.apply(item))).toList();
        }

        /**
         * 设置树级目录的级别
         *
         * @param list     原列表数据
         * @param childKey 子级key
         * @param levelKey 设置级别的key
         * @param <T>      类型
         */
        public static <T> void level(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, Integer> levelKey) {
            level(list, childKey, levelKey, 1);
        }

        /**
         * 设置树级目录的级别
         *
         * @param list     原列表数据
         * @param childKey 子级key
         * @param levelKey 设置级别的key
         * @param level    级别
         * @param <T>      类型
         */
        public static <T> void level(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, Integer> levelKey, int level) {
            list.forEach(item -> {
                levelKey.accept(item, level);
                var children = childKey.apply(item);
                if (!children.isEmpty()) {
                    level(children, childKey, levelKey, level + 1);
                }
            });
        }

        /**
         * 子节点遍历
         *
         * @param list       原列表信息
         * @param childKey   子级key
         * @param setLeafKey 设置叶子节点方法
         * @param <T>        类型
         */
        public static <T> void leaf(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, Boolean> setLeafKey) {
            list.forEach(item -> {
                var children = childKey.apply(item);
                if (!children.isEmpty()) {
                    leafChild(children, childKey, setLeafKey);
                }
            });
        }

        /**
         * 设置最后一个子节点
         *
         * @param list       原列表信息
         * @param childKey   子级key
         * @param setLeafKey 设置叶子节点方法
         * @param <T>        类型
         */
        public static <T> void leafLast(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, Boolean> setLeafKey) {
            list.forEach(item -> {
                var children = childKey.apply(item);
                if (!children.isEmpty()) {
                    leafLast(children, childKey, setLeafKey);
                } else {
                    setLeafKey.accept(item, true);
                }
            });
        }

        /**
         * 子节点遍历
         *
         * @param list       原列表信息
         * @param childKey   子级key
         * @param setLeafKey 设置叶子节点方法
         * @param <T>        类型
         */
        private static <T> void leafChild(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, Boolean> setLeafKey) {
            list.forEach(item -> {
                var children = childKey.apply(item);
                setLeafKey.accept(item, true);
                if (!children.isEmpty()) {
                    leafChild(children, childKey, setLeafKey);
                }
            });
        }

        /**
         * 设置树级目录链式内容
         *
         * @param list            原列表数据
         * @param childKey        子级key
         * @param setChainKey     链内容key
         * @param chainContentKey 内容key
         * @param <T>             类型
         */
        public static <T> void chain(List<T> list, Function<T, List<T>> childKey, BiConsumer<T, List<String>> setChainKey, Function<T, String> chainContentKey) {
            chain(list, childKey, setChainKey, chainContentKey, null);
        }

        /**
         * 设置树级目录链式内容
         *
         * @param list            原列表数据
         * @param childKey        子级key
         * @param setChainKey     链内容key
         * @param chainContentKey 内容key
         * @param chain           内容
         * @param <T>             类型
         */
        private static <T> void chain(
                List<T> list,
                Function<T, List<T>> childKey,
                BiConsumer<T, List<String>> setChainKey,
                Function<T, String> chainContentKey,
                String chain
        ) {
            list.forEach(item -> {
                var children = childKey.apply(item);
                if (StringUtils.isNotBlank(chain)) {
                    setChainKey.accept(item, Arrays.stream(chain.split(CHAIN_SEPARATOR)).toList());
                }
                if (!children.isEmpty()) {
                    // 如果有下一级，需要拼接相关数据
                    if (StringUtils.isBlank(chain)) {
                        chain(children, childKey, setChainKey, chainContentKey, chainContentKey.apply(item));
                    } else {
                        chain(children, childKey, setChainKey, chainContentKey, chain + CHAIN_SEPARATOR + chainContentKey.apply(item));
                    }
                }
            });
        }

        /**
         * 根据父级id获取子级
         *
         * @param list       列表
         * @param parentId   父级id
         * @param idKey      id key
         * @param childKey   子级 key
         * @param <TREE_VO>  树形结构
         * @param <KEY_TYPE> id 类型
         * @return 子级
         */
        public static <TREE_VO, KEY_TYPE> TREE_VO getByParentId(
                List<TREE_VO> list,
                KEY_TYPE parentId,
                Function<TREE_VO, KEY_TYPE> idKey,
                Function<TREE_VO, List<TREE_VO>> childKey
        ) {
            for (var item : list) {
                if (idKey.apply(item).equals(parentId)) {
                    return item;
                }
                var children = childKey.apply(item);
                if (children != null && !children.isEmpty()) {
                    var result = getByParentId(children, parentId, idKey, childKey);
                    if (result != null) {
                        return result;
                    }
                }
            }
            return null;
        }

    }

    /**
     * 自定义列表分组
     *
     * @param <T>
     * @param <V>
     */
    private static class CustomPartition<T, V> extends AbstractList<V> {
        final List<T> list;
        final int size;
        final Function<List<T>, V> fun;

        CustomPartition(List<T> list, int size, Function<List<T>, V> fun) {
            this.list = list;
            this.size = size;
            this.fun = fun;
        }

        @Override
        public V get(int index) {
            checkElementIndex(index, size());
            int start = index * size;
            int end = Math.min(start + size, list.size());
            return fun.apply(list.subList(start, end));
        }

        @Override
        public int size() {
            return IntMath.divide(list.size(), size, RoundingMode.CEILING);
        }

        @Override
        public boolean isEmpty() {
            return list.isEmpty();
        }
    }

}
